package org.bee.controller;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import org.bee.PageUtil;
import org.bee.ResultTool;
import org.bee.check.VoCheck;
import org.bee.cryptix.BCryptUtils;
import org.bee.enums.ResultCode;
import org.bee.enums.UserType;
import org.bee.id.UUIDUtils;
import org.bee.input.ChangePasswordVo;
import org.bee.input.DictSelectVo;
import org.bee.input.LoginVo;
import org.bee.model.AdminUser;
import org.bee.model.Element;
import org.bee.model.Permission;
import org.bee.model.result.Result;
import org.bee.output.DictSelect;
import org.bee.output.LoginSuccess;
import org.bee.output.Menus;
import org.bee.redis.RedisUtils;
import org.bee.secuirty.annotation.ApiAuth;
import org.bee.secuirty.annotation.TokenIgnore;
import org.bee.secuirty.context.CurrentUser;
import org.bee.secuirty.model.TokenInfo;
import org.bee.secuirty.model.UserInfo;
import org.bee.service.AdminUserService;
import org.bee.service.ElementService;
import org.bee.service.ext.AdminUserExtService;
import org.bee.service.ext.PermissionExtService;
import org.bee.string.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * 登录控制器
 */
@RestController
public class SystemLoginController {
    @Resource
    private AdminUserExtService adminUserExtService;
    @Resource
    private AdminUserService adminUserService;
    @Resource
    private PermissionExtService permissionExtService;
    @Resource
    private ElementService elementService;
    @Resource
    private RedisUtils redisUtils;
    @Value("${expiredTime}")
    private Integer expiredTime;

    /**
     * 系统用户登录(不鉴权)
     * @param user 用户信息
     * @return 登录结果
     */
    @TokenIgnore
    @PostMapping("login")
    public Result login(@RequestBody LoginVo user) {
        //参数校验
        Result result = VoCheck.check(user);
        if (!result.getSuccess()) return result;
        //查询用户信息
        AdminUser adminUser;
        List<AdminUser> userByUserName = adminUserExtService.getUserByUserName(user.getUsername(), UserType.SYSTEM.getCode());
        if(null == userByUserName || userByUserName.size() !=1){
            result = ResultTool.fail(ResultCode.USER_ACCOUNT_ERROR);
            result.setMsg("用户名或密码错误");
            return result;
        }
        adminUser = userByUserName.getFirst();
        //登录校验
        result = adminUserExtService.loginCheck(user, adminUser);
        if (!result.getSuccess()) return result;

        TokenInfo tokenInfo = new TokenInfo();
        String tokenKey = (String) redisUtils.get("LoginUsers:"+user.getUsername());
        //登录成功后返回一个token,token包含一些用户常规信息
        UserInfo userInfo = new UserInfo();
        BeanUtils.copyProperties(adminUser, userInfo);
        tokenInfo.setUserInfo(userInfo);
        //查用户拥有的api权限信息
        List<Permission> permissionByUserId = permissionExtService.getPermissionByUserId(adminUser.getUserId());
        List<String> userApis = new ArrayList<>();
        for (Permission permission : permissionByUserId) {
            String code = permission.getCode();
            userApis.add(code);
        }
        tokenInfo.setUserApis(userApis);

        //查用户拥有的菜单权限信息
        List<Element> elementByUserId = elementService.getMenusByUserId(adminUser.getUserId());

        List<String> userElements = new ArrayList<>();
        for (Element element : elementByUserId) {
            String code = element.getCode();
            userElements.add(code);
        }
        tokenInfo.setUserElements(userElements);
        if (null != tokenKey) {
            //用户已经登录过了
            //重置过期时间
            redisUtils.expire("LoginUserTokenInfo:" + tokenKey, expiredTime);
        } else {
            try {
                ObjectMapper objectMapper = new ObjectMapper();
                //生成令牌
                tokenKey = UUIDUtils.simpleUuid();
                //转字符串
                String value = objectMapper.writeValueAsString(tokenInfo);
                //存redis
                redisUtils.set("LoginUserTokenInfo:" + tokenKey, value, expiredTime);
                //存用户登录状态
                redisUtils.set("LoginUsers:"+user.getUsername(), tokenKey, expiredTime);
            } catch (JsonProcessingException e) {
                result = ResultTool.fail(ResultCode.TO_JSON_ERROR);
                return result;
            }
        }
        LoginSuccess loginSuccess = new LoginSuccess();
        loginSuccess.setToken(tokenKey);
        loginSuccess.setUserInfo(userInfo);
        result.setData(loginSuccess);
        return result;
    }
    /**
     * 系统用户登出
     * @param request 请求
     * @return 结果
     */
    @DeleteMapping("logout")
    public Result logout(HttpServletRequest request){
        Result result = ResultTool.success();
        String tokenInfo = request.getHeader("Authorization");
        if(tokenInfo!=null){
            //获取到了token信息（已经登录过的）
            String bearer = tokenInfo.replace("Bearer ", "");
            redisUtils.del("LoginUserTokenInfo:" + bearer);
            UserInfo user = CurrentUser.getUser();
            redisUtils.del("LoginUsers:"+user.getUsername());
        }
        return result;
    }

    /**
     * 获取用户菜单树
     * @return 菜单树
     */
    @GetMapping("getMenus")
    public Result getMenus(){
        Result result = ResultTool.success();
        UserInfo user = CurrentUser.getUser();
        List<Menus> menus = elementService.getMenusTree(user.getUserId(),null,false);
        result.setData(menus);
        return result;
    }

    /**
     * 获取用户按钮权限
     * @return 用户按钮权限
     */
    @GetMapping("getAuthElement")
    public Result getAuthElement(){
        Result result = ResultTool.success();
        UserInfo user = CurrentUser.getUser();
        HashMap<String, List<String>> authButtonByUserId = elementService.getAuthButtonByUserId(user.getUserId());
        result.setData(authButtonByUserId);
        return result;
    }
    /**
     * 修改密码
     */
    @PostMapping("changePassword")
    public Result changePassword(@RequestBody ChangePasswordVo changePasswordVo){
        Result result = ResultTool.success();
        UserInfo user = CurrentUser.getUser();
        List<AdminUser> userByUserName = adminUserExtService.getUserByUserName(user.getUsername(),UserType.SYSTEM.getCode());
        if(null == userByUserName || userByUserName.size() >1){
            result = ResultTool.fail(ResultCode.USER_ACCOUNT_ERROR);
            result.setMsg("用户不存在");
            return result;
        }
        AdminUser adminUser = userByUserName.getFirst();
        //密码校验
        if(!BCryptUtils.checkPW(changePasswordVo.getOldPassword(),adminUser.getPassword())){
            result = ResultTool.fail(ResultCode.USER_CREDENTIALS_ERROR);
            result.setMsg("旧密码错误");
            return result;
        }
        adminUser.setPassword(BCryptUtils.encrypt(changePasswordVo.getNewPassword()));
        adminUserService.update(adminUser);
        return result;
    }

    /**
     * 获取在线用户列表
     */
    @PostMapping("getOnlineUser")
    @ApiAuth(value = "user.getOnlineUser",description = "获取在线用户列表")
    public Result getOnlineUser(@RequestBody DictSelectVo dictSelectVo){
        Result result = ResultTool.success();
        try {
            PageUtil.setPage(dictSelectVo.getCurrent(),dictSelectVo.getSize());
            long keyCount = 1L;
            Set<String> keys = new HashSet<>();
            if(StringUtils.isEmpty(dictSelectVo.getLabel())){
                keys = redisUtils.keysPage("LoginUsers:*",
                        dictSelectVo.getCurrent(), dictSelectVo.getSize());
                keyCount = redisUtils.getKeyCount("LoginUsers:*");
            }else{
                String key = "LoginUsers:"+dictSelectVo.getLabel();
                keys.add(key);
            }

            List<DictSelect> onlineUserList = new ArrayList<>();
            for (String key : keys) {
                DictSelect user = new DictSelect();
                String userToken = (String) redisUtils.get(key);
                if(StringUtils.isNotEmpty(userToken)){
                    String label = key.replaceAll("LoginUsers:", "");
                    user.setLabel(label);
                    user.setValue(userToken);
                    onlineUserList.add(user);
                }else{
                    keyCount = 0;
                }
            }
            PageUtil.getPage().setTotal(keyCount);
            result.setPageData(onlineUserList);
        } catch (Exception e) {
            result = ResultTool.fail();
            result.setMsg("查询异常");
            return result;
        } finally {
            PageUtil.clearPage();
        }
        return result;
    }

    /**
     * 强制登出
     * @param token 令牌
     * @return 登出结果
     */
    @DeleteMapping("forceLogout")
    @ApiAuth(value = "user.forceLogout",description = "强制登出")
    public Result forceLogout(String token){
        Result result = ResultTool.success();
        String tokenInfo = (String) redisUtils.get("LoginUserTokenInfo:" + token);
        ObjectMapper mapper = new ObjectMapper();
        TokenInfo user;
        try {
            user = mapper.readValue(tokenInfo, TokenInfo.class);
        } catch (JsonProcessingException e) {
            result = ResultTool.fail(ResultCode.DATA_NULL);
            result.setMsg("未查询到登录信息，登出失败");
            return result;
        }
        String username = user.getUserInfo().getUsername();
        redisUtils.del("LoginUserTokenInfo:" + token);
        redisUtils.del("LoginUsers:"+username);
        return result;
    }
}
